package roborally.program.commands;

import be.kuleuven.cs.som.annotate.*;
import roborally.model.Robot;

/**
 * A class for dealing with sequences: (seq(command)(command)...).     
 * 
 * @invar	The array of commands of each selection must be a valid list of commands.
 * 			| canHaveAsCommands(getCommands())
 * 
 * @version	1.0
 * @author  Pieter Robberechts	(1e Bach Informatica)
 * @author	Jeroen Sanders		(1e Bach Informatica)
 *
 */
public class Sequence extends Command {

	/**
	 * Initialize this new sequence with a line number and an array of commands.
	 * 			
	 * @param 	commands
	 * 			The list of commands which the sequence has to execute.
	 * @pre		The given array of commands must be valid.
	 * 			| canHaveAsCommands(commands)
	 * @post	The array of commands of this sequence is set to the given array of commands.
	 * 			| new.getCommands() == commands
	 */
	public Sequence(int lineNb, Command[] commands) {
		super(lineNb);
		assert(canHaveAsCommands(commands));
		this.commands = commands;
	}
	
	/**
	 * Variable registering the list of commands associated with this sequence.
	 */
	private final Command[] commands;
	
	/**
	 * Return the array of commands of this sequence.
	 */
	@Basic @Raw @Immutable
	public Command[] getCommands() {
		return commands;
	}

	/**
	 * Check whether a given list of commands is a valid one for a sequence.
	 * 
	 * @param 	commands
	 * 			The list of commands to check for whether it is a valid one.
	 * @return	Return false if the given array of commands is ineffective.
	 * 			| if(commands == null)
	 * 			| 	then result == false
	 * @return	Return false if at least one of the commands in the given array 
	 * 			is ineffective; else return true.
	 * 			| for one command in commands : 
	 * 			| 	if(command == null)
	 * 			|		then result == false
	 * 			| result == true	
	 */
	@Raw
	public boolean canHaveAsCommands(Command[] commands) {
		if(commands == null)
			return false;
		for(Command command : commands)
			if(command == null)
				return false;
		return true;
	}
	
	/**
	 * Return a textual representation of this sequence,
	 * formatted in accordance with its position in the program.
	 * 
	 * @return	The result is a pretty formatted string of this sequence,
	 * 			taking in account his relative position in the program.
	 * 			| result == 
	 * 			| 	getIndentation(indentationLevel) + "(seq\n" + 
	 * 			|	{ for each command in getCommands():
	 *			|		command.prettyFormat(indentationLevel+1) + "\n" 
	 *			|	} 
	 *			|	+ getIndentation(indentationLevel) + ")"
	 */
	@Override
	public String prettyFormat(int indentationLevel) {
		String commandList = "";
		for(Command command : getCommands())
			commandList += command.prettyFormat(indentationLevel+1) + "\n";
		return getIndentation(indentationLevel) + "(seq\n" + 
				commandList + 
				getIndentation(indentationLevel) + ")";
	}

	/**
	 * Return the next basic command for a robot, given the last executed line number. 
	 * 
	 * @return	Return false if the given line number is larger than or equal to 
	 * 			the line number of this sequence.
	 * 			| if( lineNb >= getLineNb() )
	 * 			|	then result == false
	 * @return	Search the next basic command in the array of commands of this sequence and
	 * 			return the result of this search (null if the next basic command wasn't found
	 * 			in this selection).
	 * 			| for one i in commands.length : 
	 * 			|	if( getCommands()[i].getNextBasicCommand(lineNb, robot) != null)
	 * 			|		then result == getCommands()[i].getNextBasicCommand(lineNb, robot)
	 * 			|	else
	 * 			|		then result == null
	 */
	@Override
	public BasicCommand getNextBasicCommand(int lineNb, Robot robot) {
		// The next basic command has to be searched outside of this sequence
		if(lineNb >= getLineNb())
			return null;
		
		// The next basic command might be an element of this loop
		for(int i = 0; i < commands.length; i++) {
			BasicCommand next = getCommands()[i].getNextBasicCommand(lineNb, robot);
			if(next != null)
				return next;
		}
		return null;
	}

}
